﻿#ifndef QFLOWCHART_H
#define QFLOWCHART_H

#include <QtCore>
#include <QtGui>
#include <QWidget>
#include <QMenu>
#include <QDomDocument>
#include <QVariant>
#include <QMap>

#include "QFlowChartStyle.h"
#include "QInsertionPoint.h"
#include "FlowChart_Global.h"

namespace CVV
{

namespace FlowChart
{

class QBlock;

class CVV_FLOWCHART_EXPORT QFlowChart : public QWidget
{
    Q_OBJECT

public:
    enum {Display, Selectable, Insertion};
    enum {Single, Multiple};
    int m_iBlockNum;    // block Id

    explicit QFlowChart(QMenu *blockMenu, QWidget *parent = nullptr);
    ~QFlowChart() override;

    QDomDocument document() const;

    QDomDocument getCurBlockDocument(QBlock *curBlock);

    /**
     * @brief targetPoint
     * @return
     */
    QInsertionPoint targetPoint() const;

    /**
     * @brief retranslateUi 翻译UI显示文本
     */
    void retranslateUi(){}

    /**
     * @brief 设置/获取流程关联文件路径；
     */
    void setFlowFilePath(const QString &path);
    QString getFlowFilePath() const;

    /**
     * @brief checkParallelBlock 检查当前界面模块类型，判断是否是平行分支
     */
    int checkParallelBlock(QMouseEvent *evt, int &branchId);

    /**
     * @brief checkContainerBlock 判断是否是容器节点
     */
    bool checkContainerBlock(QMouseEvent *evt, bool &isExpand);
    /**
     * @brief root
     * @return
     */
    QBlock* root() const;

    /**
     * @brief activeBlock
     * @return
     */
    QBlock* activeBlock() const;

    /**
     * @brief getBlock 根据插件单元code和单元Id 获取当前单元对象
     * @param blockCode 插件单元Code
     * @param Id 单元ID
     * @return
     */
    QBlock* getBlock(const QString &blockCode, const QString &cellId);

    /**
     * @brief getBlock 根据插件单元code 获取当前单元对象列表
     * @param blockCode 插件单元Code
     * @return
     */
    QList<QPair<QBlock*, QString>> getBlock(const QString &blockCode);

    /**
     * @brief zoom
     * @return
     */
    double zoom() const;

    /**
     * @brief setZoom
     * @param aZoom
     */
    void setZoom(const double aZoom);

    /**
     * @brief status
     */
    int status() const;

    /**
     * @brief setStatus
     * @param aStatus
     */
    void setStatus(int aStatus);

    /**
     * @brief deleteBlock
     * @param aBlock
     */
    void deleteBlock(QBlock *aBlock);

    /**
     * @brief getNearistPoint  获取最近的插入点
     * @param x
     * @param y
     * @return
     */
    QInsertionPoint getNearistPoint(int x, int y) const;

    /**
     * @brief regeneratePoints 重新生成插入点
     */
    void regeneratePoints();

    /**
     * @brief generatePoints
     * @param aBlock
     */
    void generatePoints(QBlock *aBlock);

    /**
     * @brief buffer
     * @return
     */
    QString buffer() const;

    /**
     * @brief setBuffer
     * @param aBuffer
     */
    void setBuffer(const QString & aBuffer);

    /**
     * @brief setMultiInsert
     * @param aValue
     */
    void setMultiInsert(bool aValue);

    /**
     * @brief multiInsert
     * @return
     */
    bool multiInsert() const;

    /**
     * @brief chartStyle
     * @return
     */
    QFlowChartStyle chartStyle() const;

    /**
     * @brief setChartStyle
     * @param aStyle
     */
    void setChartStyle(const QFlowChartStyle & aStyle);

    /**
     * @brief toString
     * @return
     */
    QString toString();

    /**
     * @brief fromString
     * @param str
     */
    bool fromString(const QString & str);

    /**
     * @brief canUndo
     * @return
     */
    bool canUndo() const;

    /**
     * @brief canRedo
     * @return
     */
    bool canRedo() const;

    /**
     * @brief canPaste
     * @return
     */
    bool canPaste() const;

    /**
     * @brief undo 撤销
     */
    void undo();

    /**
     * @brief makeUndo
     */
    void makeUndo();

    /**
     * @brief redo 重做
     */
    void redo();

    /**
     * @brief makeChanged
     */
    void makeChanged();

    /**
     * @brief makeBackwardCompatibility 向后兼容
     */
    void makeBackwardCompatibility();

    /**
     * @brief getBackgroundPixmap
     * @return
     */
    QPixmap getBackgroundPixmap() const;

    /**
     * @brief setBackgroundPixmap
     * @param value
     */
    void setBackgroundPixmap(const QPixmap &value);

    /**
     * @brief paintTo
     * @param canvas
     */
    void paintTo(QPainter *canvas);

    /**
     * @brief modifyParallel
     * @param block
     * @param num
     * @param bAdd
     */
    void modifyParallel(QBlock *block, int num, bool bAdd = true, const QList<int>& lstDeleteIndex = QList<int>());

    /**
     * @brief updateParallelBranchStatus 更新平行分支状态
     * @param block
     * @param lstEnableIndex
     */
    void updateParallelBranchStatus(QBlock *block, const QList<int>& lstEnableIndex);

    /**
     * @brief calcLength
     * @param p1
     * @param p2
     * @return
     */
    static double calcLength(const QPointF & p1, const QPointF & p2);

    /**
     * @brief drawBottomArrow
     * @param canvas
     * @param aPoint
     * @param aSize
     */
    static void drawBottomArrow(QPainter *canvas, const QPointF & aPoint, const QSizeF & aSize);

    /**
     * @brief drawRightArrow
     * @param canvas
     * @param aPoint
     * @param aSize
     */
    static void drawRightArrow(QPainter *canvas, const QPointF & aPoint, const QSizeF & aSize);

    /**
     * @brief setIsPaste 是否是粘贴操作
     * @param value
     */
    void setIsPaste(bool value);

    /**
      * @brief setCtrlEnable 设置是否按下ctrl
      * @param
      * @return void
      */
    void setCtrlEnable(bool value);

    /**
      * @brief getCtrlEnable 获取是否按下ctrl
      * @param
      * @return bool
      */
    bool getCtrlEnable();

    /**
      * @brief checkMultBlocksSelected校验流程图中选择项是否为多个
      * @param
      * @return int
      */
    void checkMultBlocksSelected(int &selectType);

    /**
      * @brief getSelectBlockNum 获取当前节点下选中的Block数量
      * @param
      * @return
      */
    void getSelectBlockNum(QBlock *Block, int &count);

    /**
      * @brief getSelectBlocks 获取当前选择节点
      * @param
      * @return QList<QBlock *>
      */
    QList<QBlock *> getSelectBlocks();

    /**
      * @brief addSelectSubBlocks 将选中的子模块添加进去
      * @param
      * @return
      */
    void addSelectSubBlocks(QBlock *targetBlock, QList<QBlock *> &blockLst);

    /**
      * @brief updateFindTargetBlocks 更新当前流程图中查找的blocks
      * @param keyStr
      * @return
      */
    void updateFindTargetBlocks(int searchCode, QString &keyStr);
    void getTargetBlocksCount(int &Count);

    /**
      * @brief getFindTargetBlocksCount 获取查找Block的数量
      * @param
      * @return int
      */
    int getFindTargetBlocksCount();

    /**
      * @brief searchBlockChanged 当前查找到的Block进行切换
      * @param
      * @return
      */
    void searchBlockChanged(int val);

    /**
      * @brief detectUnreferencedCells 检测未引用节点
      */
    void detectUnreferencedCells();

    /**
      * @brief detectSelectedReferenceCurCell 检测出引用当前选中节点的节点
      */
    void detectSelectedReferenceCurCell();

    /**
      * @brief setContainBlockCollapseOrExpand 设置节点的展开折叠
      */
    void setContainBlockCollapseOrExpand(bool expand);

    /**
      * @brief getUnReferencedIdLst 获取未被使用节点id链表
      */
    QStringList getUnReferencedIdLst();
    /**
      * @brief getUnReferencedIdLst 获取引用当前节点的Id链表
      */
    QStringList getReferencedIdLst();

    /**
      * @brief getUnReferencedIdLst 获取未被使用节点id链表
      */
    void SetUnReferencedIdLst(QStringList strLst);

    /**
     * @brief isSubprocessFlow 是不是子流程
     * @return
     */
    bool isSubprocessFlow();

    /**
     * @brief getActiveBlockParamValueByCode 根据参数code获取参数值
     * @param code
     * @return
     */
    QVariant getActiveBlockParamValueByCode(const QString &code);

    /**
     * @brief getActiveBlockParam 获取参数
     * @param pluginCode 返回block对应的插件code
     * @return
     */
    QVariant getActiveBlockParam(QString &pluginCode) const;

    /**
     * @brief getActiveBlockId 当前节点ID
     * @return
     */
    QString getActiveBlockId() const;

    /**
     * @brief setId 流程图ID
     * @param id
     */
    void setId(int id);
    int getId() const;

    //多个删除
    void deleteSelectBlockLst(QList<QBlock*> &delBlockLst);

    /**
     * @brief updateBlockPreId 更新前节点id
     */
    void updateBlockPreId();

    /**
      * @brief setCurParallelNum 设置平行分支选择分支号
      * @param int
      */
    void setCurParallelNum(int selectNum);

    /**
      * @brief getCurParallelNum 获取平行分支当前选择分支号
      * @return int
      */
    int getCurParallelNum();

    /**
      * @brief setFlowChartMenuShowState 设置菜单显示状态
      * @param bool state
      */
    void setFlowChartMenuShowState(bool state);

    /**
      * @brief getFlowChartMenuShowState 获取菜单显示状态
      * @return bool
      */
    bool getFlowChartMenuShowState();

    /**
      * @brief resetBlockRunState 还原运行状态
      * @return
      */
    void resetBlockRunState();

    /**
      * @brief resetChildBlockRunState 还原运行状态
      */
    void resetChildBlockRunState(QBlock *pBlock);

    /**
     * @brief checkMeetSubProcessConditions 校验满足子流程条件, 是否是多选, 0:不是多选, 1:多选, 2:满足子流程选择多个连续节点
     */
    int checkMeetSubProcessConditions();

    /**
     * @brief  通过节点Id, 获取节点对象
     */
    QBlock* getTargetBlockById(const QString id, bool isPre = false);
    void findChildTargetBlockId(QBlock* ParentBlock, QBlock*& targetBlock, const QString id, bool isPre = false);

    /**
     * @brief 插入子方案数据节点
     */
    bool insertSubProcessBlockByPreId(QBlock* replaceBlock, QDomElement& ele);

    /**
     * @brief 更新节点结果
     */
    void updateCellResultMap(QMap<int, bool> resultMap);

    /**
     * @brief 获取节点更新结果
     */
    int getCellResultState(const int cellId);
signals:
    void zoomChanged(const double aZoom);
    void statusChanged();
    void editBlock(QBlock *block);
    void changed();
    void modified();
    void sigSelectBlock(QBlock *block);
    void sigInsertNode(QBlock *block, bool isPaste = false);
    void moveChanged(int x, int y);
    void signalCurFoundBlockChanged(int iX, int iY, int width, int height);
    void sigFoundBlocksResult(int count, int index);
    void sigFoundReset();                                                       //搜索结果重置；

    /**
     * @brief sigRedoUndo 撤销重做
     * @param sText
     */
    void sigRedoUndo(const QString &sText);

    void sigSizeChange();

    void sigUpdageAerialView();

    void sigUpdateAerialScaled();

    /**
     * @brief 修改流程插件中子流程文件路径
     */
    void sigUpdatePluginSubProcessFile(int cellId, QString filePath, QString titleStr);

    /**
     * @brief 增加子流程数据节点
     */
    void sigAddSubProcessDataItem(QString parentFilePath, QString modelFilePath, QString idStr, QString& targetProcessPath);

    /**
     * @brief 增加子流程创建节点
     */
    void sigCreateSubProcess(QString &subFilePath, QString idStr);

    /**
     * @brief 增加子方案数据节点
     */
    void sigAddSubProcessData(QString parentFilePath, QString subFilePath);

    /**
     * @brief 增加平行分支节点
     */
    void sigCreateParallelBranch(int &branchNum, QString idStr);

public slots:
    /**
     * @brief slotSelectAll
     */
    void slotSelectAll();

    /**
     * @brief slotDelAllBlocksSelectState 删除所有Block选中状态
     */
    void slotDelAllBlocksSelectState();

protected:
    void paintEvent(QPaintEvent *pEvent) override;
    void mousePressEvent(QMouseEvent *pEvent) override;
    void mouseMoveEvent(QMouseEvent *pEvent) override;
    void mouseDoubleClickEvent(QMouseEvent * event) override;
    void resizeEvent(QResizeEvent *event) override;

private:
    /**
     * @brief clear
     */
    void clear();

    /**
     * @brief realignObjects 重新排列节点
     */
    void realignObjects();

    /**
      * @brief updateParentBlockSelectedState 校验父节点是否被选中
      * @param QBlock *
      */
    void updateParentBlockSelectedState(QBlock *block);

    /**
     * @brief delCurBlockChildBlockSelectedState 去除当前block节点下所有子节点中选中状态
     * @param block
     */
    void delCurBlockChildBlockSelectedState(QBlock *block);

    virtual QSize sizeHint() const override;

    /**
     * @brief getChildBlock 获取子block
     * @param block
     * @param blockCode
     * @param cellId
     * @return
     */
    QBlock* getChildBlock(QBlock *block, const QString &blockCode, const QString &cellId);

    /**
      * @brief findBlocksByKeyWord 根据关键字查找某个节点下满足条件的节点，存放到m_findTargetBlockLst里
      * @param inBlock, keyStr
      * @return
      */
    void findBlocksByKeyWord(QBlock *inBlock, const QString &keyStr, const int &searchType = 0);

    /**
      * @brief updateBlocksReferenceStatus 更新节点的被使用状态
      */
    void updateBlocksReferenceStatus(QBlock *inBlock, QStringList &allCurIdLst, QStringList &allPreIdLst);

    /**
      * @brief updateBlocksReferenceCurBlock 更新激活节点被引用节点情况
      */
    void updateBlocksReferenceCurBlock(QBlock *inBlock, QString targetStr);

    /**
      * @brief updateBlocksExpandState 更新节点展开情况
      */
    void updateBlocksExpandState(QBlock *inBlock, bool isExpand);

    bool setCellParamReferenceCensus(QString &strVal);

    /**
     * @brief checkInsertSubProcessNode 判断插入是否是子流程节点
     */
    bool checkInsertSubProcessNode(QDomElement &domEle, QString &subPath, QString &plugInId);

    /**
     * @brief checkInsertParallelNode 判断插入是否是平行分支节点
     */
    bool checkInsertParallelNode(QDomElement &domEle, QString &plugInId);

    /**
     * @brief modifySubProcessNodeValue 修改导入子流程路径
     */
    void modifySubProcessNodeValue(QDomElement &domEle, QString modifyStr);

    /**
     * @brief updateSubProcessXmlTitle 修改子流程xml中title名称
     */
    void updateSubProcessXmlTitle(QDomElement &domEle, const QString titleStr);

    /**
     * @brief updateParallelXmlBranchNum 修改平行分支xml中分支数量
     */
    void updateParallelXmlBranchNum(QDomDocument& doc, QDomElement &domEle, const QString numStr, const int iNum);

private:
    double m_dZoom{1};                                      // 缩放因子
    int m_iStatus{Display};                                 // 当前状态
    int m_parallelNum{-1};                                  // 当前选择平行分支编号
    int m_id{0};                                            // 流程id
    QString m_flowFilePath{""};                             // 流程文件路径
    QString m_strBuffer{""};                                // 节点缓存数据字符串

    QBlock *m_pRoot{nullptr};                               // 根节点
    QBlock *m_pActiveBlock{nullptr};                        // 当前选中节点
    QMenu *m_pFlowChartMenu{nullptr};                       // 右键菜单

    bool m_bIsMultiInsert{false};                           // 是否多点插入
    bool m_bIsPaste{false};                                 // 是否是粘贴操作
    bool m_ctrlEnable{false};                               // ctrl 使能
    bool m_menuShow{false};                                 // 菜单显示
    bool m_bIsUpdateAerialView{false};                      // 鸟瞰图更新
    bool m_bIsScaled{false};                                // 是否缩放

    QInsertionPoint m_targetPoint;                          // 插入点
    QFlowChartStyle m_chartStyle;                           // 当前样式
    QPixmap  m_backgroundPixmap;                            // 背景图
    QPointF m_curSelectedPos;                               // 当前选中点
    QInsertionPoint m_delInsertPt;                          // 删除的插入点

    QList<QInsertionPoint> m_lstinsertionPoints;            // 插入点列表
    QStack<QVariant> m_undoStack;                           // 撤销操作
    QStack<QVariant> m_redoStack;                           // 恢复操作
    QList<QBlock*> m_findTargetBlockLst;                    // 查找目标Block
    QStringList m_unReferencedIdLst;                        // 未引用Block id list
    QMap<QString, int> m_cellReferencedMap;                 // 每个节点引用次数统计
    QStringList m_referencedIdLst;                          // 引用当前选中节点的节点　ｉd list, 第一个默认为当前节点
    QMap<int, bool> m_cellResultMap;                        // 节点执行结果
};

}

}
#endif // QFLOWCHART_H
